/*
 ------------------------------------------------------------------------------
 Copyright (C) 2009-2011 Eternal Games.

 This file is part of the Eternal Tech source code.

 The Eternal Tech source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Eternal Tech source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Eternal Tech source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// cl_entities.cpp - ...
//


#include "cl_local.h"
#include "client.h"


/*
 ==============================================================================

    FRAME PARSING

 ==============================================================================
*/


/*
 ==================
 CL_EntityEvent

 TODO: Particle effects
 ==================
*/
void CL_EntityEvent ()
{
	entity_state_t  *state;
    int				i;

	for (i = 0; i < cl.frame.numEntities; i++)
	{
        state = &cl.parseEntities[(cl.frame.parseEntitiesIndex+i) & (MAX_PARSE_ENTITIES-1)];

		// EF_TELEPORTER acts like an event, but is not cleared each
		// frame
		//if (state->effects & EF_TELEPORTER)
		//	CL_TeleporterParticles(state->origin);

		if (!state->event)
			continue;

		switch (state->event){
		case EV_ITEM_RESPAWN:
			S_StartSound(NULL, state->number, CHAN_WEAPON, S_RegisterSound("items/respawn1.wav"), 1, ATTN_IDLE, 0);
			//CL_ItemRespawnParticles(state->origin);

			break;
		case EV_PLAYER_TELEPORT:
			S_StartSound(NULL, state->number, CHAN_WEAPON, S_RegisterSound("misc/tele1.wav"), 1, ATTN_IDLE, 0);
			//CL_TeleportParticles(state->origin);

			break;
		case EV_FOOTSTEP:
			S_StartSound(NULL, state->number, CHAN_BODY, cl.media.sfxFootSteps[rand()&3], 1, ATTN_NORM, 0);

			break;
		case EV_FALLSHORT:
			S_StartSound(NULL, state->number, CHAN_AUTO, S_RegisterSound("player/land1.wav"), 1, ATTN_NORM, 0);

			break;
		case EV_FALL:
			S_StartSound(NULL, state->number, CHAN_AUTO, S_RegisterSound("*fall2.wav"), 1, ATTN_NORM, 0);

			break;
		case EV_FALLFAR:
			S_StartSound(NULL, state->number, CHAN_AUTO, S_RegisterSound("*fall1.wav"), 1, ATTN_NORM, 0);

			break;
		}
	}
}


/*
 ==============================================================================

    SENDS ENTITIES TO THE RENDERER EVERY FRAME

 ==============================================================================
*/


/*
 ==================
 CL_AddShellEntity
 ==================
*/
static void CL_AddShellEntity (renderEntity_t *ent, centity_t *cent, uint effects, bool emitLight)
{
	vec3_t  realOrigin;
	vec3_t	rgb;

	if (!(effects & (EF_PENT|EF_QUAD|EF_DOUBLE|EF_HALF_DAMAGE|EF_COLOR_SHELL)))
		return;

	if (cent->current.number == cl.playernum + 1)
		VectorCopy(cl.renderView.viewOrigin, realOrigin);
	else
		VectorCopy(ent->origin, realOrigin);

	if (effects & EF_PENT)
	{
		ent->customMaterial = cl.media.invulnerabilityShellMaterial;
		MakeRGBA(ent->materialParms, 1.0f, 0.0f, 0.0f, 0.3f);

		R_AddEntityToScene(ent);

		if (emitLight && !(ent->renderFX & RF_VIEWERMODEL))
			CL_DynamicLight(realOrigin, 200.0f + (rand() & 31), 1.0f, 0.0f, 0.0f, false, 0);
	}

	if (effects & EF_QUAD)
	{
		ent->customMaterial = cl.media.quadDamageShellMaterial;
		MakeRGBA(ent->materialParms, 0.0f, 0.0f, 1.0f, 0.3f);

		R_AddEntityToScene(ent);

		if (emitLight && !(ent->renderFX & RF_VIEWERMODEL))
			CL_DynamicLight(realOrigin, 200.0f + (rand() & 31), 0.0f, 0.0f, 1.0f, false, 0);
	}

	if (effects & EF_DOUBLE)
	{
		ent->customMaterial = cl.media.doubleDamageShellMaterial;
		MakeRGBA(ent->materialParms, 0.9f, 0.7f, 0.0f, 0.3f);

		R_AddEntityToScene(ent);

		if (emitLight && !(ent->renderFX & RF_VIEWERMODEL))
			CL_DynamicLight(realOrigin, 200.0f + (rand() & 31), 0.9f, 0.7f, 0.0f, false, 0);
	}

	if (effects & EF_HALF_DAMAGE)
	{
		ent->customMaterial = cl.media.halfDamageShellMaterial;
		MakeRGBA(ent->materialParms, 0.55f, 0.6f, 0.45f, 0.3f);

		R_AddEntityToScene(ent);

		if (emitLight && !(ent->renderFX & RF_VIEWERMODEL))
			CL_DynamicLight(realOrigin, 200.0f + (rand() & 31), 0.56f, 0.59f, 0.45f, false, 0);
	}

	if (effects & EF_COLOR_SHELL)
	{
		ent->customMaterial = cl.media.genericShellMaterial;
		MakeRGBA(ent->materialParms, 0.0f, 0.0f, 0.0f, 0.3f);

		VectorClear(rgb);

		if (ent->renderFX & RF_SHELL_RED)
		{
			ent->materialParms[MATERIALPARM_RED] = 1.0f;
			rgb[0] = 1.0f;
		}

		if (ent->renderFX & RF_SHELL_GREEN)
		{
			ent->materialParms[MATERIALPARM_GREEN] = 1.0f;
			rgb[1] = 1.0f;
		}

		if (ent->renderFX & RF_SHELL_BLUE)
		{
			ent->materialParms[MATERIALPARM_BLUE] = 1.0f;
			rgb[2] = 1.0f;
		}

		R_AddEntityToScene(ent);

		if (emitLight && !(ent->renderFX & RF_VIEWERMODEL))
			CL_DynamicLight(realOrigin, 200.0f + (rand() & 31), rgb[0], rgb[1], rgb[2], false, 0);
	}

	// Make sure these get reset
	ent->customMaterial = NULL;
	MakeRGBA(ent->materialParms, 1.0f, 1.0f, 1.0f, 1.0f);
}

/*
 ==================
 CL_AddEntityTrails

 TODO: Add the particle effects back
 ==================
*/
static void CL_AddEntityTrails (centity_t *cent, renderEntity_t *ent, uint effects)
{
	int TODO;

	if (effects & EF_ROCKET)
	{
		CL_RocketTrail(cent->lerpOrigin, ent->origin, cl.media.rocketTrailParticle, cl.media.rocketTrailParticle2);
		CL_DynamicLight(ent->origin, 200.0f, 1.0f, 1.0f, 0.0f, false, 0);
	}
	else if (effects & EF_BLASTER)
	{
		if (effects & EF_TRACKER)
		{
			CL_BlasterTrail(cent->lerpOrigin, ent->origin, cl.media.blasterTrailParticle);
			CL_DynamicLight(ent->origin, 200.0f, 0.0f, 1.0f, 0.0f, false, 0);
		}
		else
		{
			CL_BlasterTrail(cent->lerpOrigin, ent->origin, cl.media.blasterTrailParticle);
			CL_DynamicLight(ent->origin, 200.0f, 1.0f, 1.0f, 0.0f, false, 0);
		}
	}
	else if (effects & EF_HYPERBLASTER)
	{
		if (effects & EF_TRACKER)
			CL_DynamicLight(ent->origin, 200.0f, 0.0f, 1.0f, 0.0f, false, 0);
		else
			CL_DynamicLight(ent->origin, 200.0f, 1.0f, 1.0f, 0.0f, false, 0);
	}
	else if (effects & EF_GIB)
		TODO;
		//CL_BloodTrail(cent->lerpOrigin, ent->origin, false);
	else if (effects & EF_GREENGIB)
		TODO;
		//CL_BloodTrail(cent->lerpOrigin, ent->origin, true);
	else if (effects & EF_GRENADE)
		CL_GrenadeTrail(cent->lerpOrigin, ent->origin, cl.media.grenadeTrailParticle);
	else if (effects & EF_FLIES)
		TODO;
		//CL_FlyEffect(cent, ent->origin);
	else if (effects & EF_TRAP)
	{
		//CL_TrapParticles(ent->origin);
		CL_DynamicLight(ent->origin, 100.0f + (rand() % 100), 1.0f, 0.8f, 0.1f, false, 0);
	}
	else if (effects & EF_FLAG1)
	{
		//if (!(ent->renderFX & RF_VIEWERMODEL))
		//	CL_FlagTrail(cent->lerpOrigin, ent->origin, 1.0, 0.1, 0.1);

		CL_DynamicLight(ent->origin, 225.0f, 1.0f, 0.1f, 0.1f, false, 0);
	}
	else if (effects & EF_FLAG2)
	{
		//if (!(ent->renderFX & RF_VIEWERMODEL))
		//	CL_FlagTrail(cent->lerpOrigin, ent->origin, 0.1, 0.1, 1.0);

		CL_DynamicLight(ent->origin, 225.0f, 0.1f, 0.1f, 1.0f, false, 0);
	}
	else if (effects & EF_TAGTRAIL)
	{
		//if (!(ent->renderFX & RF_VIEWERMODEL))
		//	CL_TagTrail(cent->lerpOrigin, ent->origin);

		CL_DynamicLight(ent->origin, 225.0f, 1.0f, 1.0f, 0.0f, false, 0);
	}
	else if (effects & EF_TRACKERTRAIL)
	{
		if (ent->renderFX & RF_VIEWERMODEL)
			CL_DynamicLight(ent->origin, 255.0f, -1.0f, -1.0f, -1.0f, false, 0);
		else
		{
			if (effects & EF_TRACKER)
				CL_DynamicLight(ent->origin, 50.0f + (500 * (sin(cl.time / 500.0f) + 1.0f)), -1.0f, -1.0f, -1.0f, false, 0);
			else
			{
				//CL_TrackerShellParticles(cent->lerpOrigin);
				CL_DynamicLight(ent->origin, 155.0f, -1.0f, -1.0f, -1.0f, false, 0);
			}
		}
	}
	else if (effects & EF_TRACKER)
	{
		//CL_TrackerTrail(cent->lerpOrigin, ent->origin);
		CL_DynamicLight(ent->origin, 200.0f, -1.0f, -1.0f, -1.0f, false, 0);
	}
	else if (effects & EF_IONRIPPER)
		CL_DynamicLight(ent->origin, 100.0f, 1.0f, 0.5f, 0.5f, false, 0);
	else if (effects & EF_BLUEHYPERBLASTER)
		CL_DynamicLight(ent->origin, 200.0f, 0.0f, 0.0f, 1.0f, false, 0);
}

/*
 ==================
 CL_AddEntityModels

 TODO: Comment some stuff
 ==================
*/
static void CL_AddEntityModels (renderEntity_t ent, centity_t *cent, entity_state_t *state)
{
    clientInfo_t   *ci;
	bool			isClient;
	int				weapon;

	// If set to invisible, skip
	if (!state->modelindex)
	{
		VectorCopy(ent.origin, cent->lerpOrigin);
		return;
	}

	// Create a new entity
	ent.reType = RE_MODEL;
	ent.renderFX = state->renderfx;
	ent.scale = state->scale;

	// Skip drawing the player model in first person view
	isClient = state->number == cl.playernum+1;
	if (isClient)
		ent.renderFX |= RF_VIEWERMODEL;

	// Check model indices
	if (state->modelindex)
	{
		if (state->modelindex == 255)
		{
			// Use a custom player skin
			ci = &cl.clientInfo[state->skinnum & 255];
			if (!ci->valid)
				ci = &cl.baseClientInfo;

			ent.skinIndex = 0;
			ent.model = ci->model;
			ent.customMaterial = ci->skin;

			if (state->renderfx & RF_USE_DISGUISE)
			{
				if (!Str_ICompareChars(ci->info, "male", 4))
					ent.customMaterial = R_RegisterMaterial("players/male/disguise", true);
				else if (!Str_ICompareChars(ci->info, "female", 6))
					ent.customMaterial = R_RegisterMaterial("players/female/disguise", true);
				else if (!Str_ICompareChars(ci->info, "cyborg", 6))
					ent.customMaterial = R_RegisterMaterial("players/cyborg/disguise", true);
			}
		}
		else
		{
			ent.skinIndex = state->skinnum;
			ent.model = cl.media.gameModels[state->modelindex];
			ent.customMaterial = NULL;
		}

		R_AddEntityToScene(&ent);

		CL_AddShellEntity(&ent, cent, state->effects, true);
	}

	if (state->modelindex2)
	{
		if (state->modelindex2 == 255)
		{
			// Use a custom weapon
			ci = &cl.clientInfo[state->skinnum & 255];
			if (!ci->valid)
				ci = &cl.baseClientInfo;

			weapon = state->skinnum >> 8;
			if (weapon < 0 || weapon >= MAX_CLIENTWEAPONMODELS)
				weapon = 0;

			if (ci->weaponModel[weapon])
				ent.model = ci->weaponModel[weapon];
			else
				ent.model = ci->weaponModel[0];

		}
		else
			ent.model = cl.media.gameModels[state->modelindex2];

		R_AddEntityToScene(&ent);

		CL_AddShellEntity(&ent, cent, state->effects, false);
	}

	if (state->modelindex3)
	{
		ent.model = cl.media.gameModels[state->modelindex3];
		R_AddEntityToScene(&ent);

		CL_AddShellEntity(&ent, cent, state->effects, false);
	}

	if (state->modelindex4)
	{
		ent.model = cl.media.gameModels[state->modelindex4];
		R_AddEntityToScene(&ent);

		CL_AddShellEntity(&ent, cent, state->effects, false);
	}
}

/*
 ==================
 CL_AddPacketEntities
 ==================
*/
void CL_AddPacketEntities ()
{
	renderEntity_t	 ent;
	centity_t		 *cent;
    entity_state_t   *state;
	vec3_t			 angles, autoRotateAxis[3];
	float			 scale;
	int				 i;

	// Some items auto-rotate
	VectorSet(angles, 0, AngleMod(cl.time * 0.1f), 0);
	AnglesToAxis(angles, autoRotateAxis);

	// Reset the entity
	memset(&ent, 0, sizeof(renderEntity_t));

	// Run through the entities
	for (i = 0; i < cl.frame.numEntities; i++)
	{
		state = &cl.parseEntities[(cl.frame.parseEntitiesIndex+i) & (MAX_PARSE_ENTITIES-1)];

		cent = &cl.entities[state->number];

		// Interpolate origin
		if (state->renderfx & RF_FRAMELERP)
			VectorLerp(cent->current.old_origin, cent->current.origin, cl.lerpFrac, ent.origin);
		else
			VectorLerp(cent->prev.origin, cent->current.origin, cl.lerpFrac, ent.origin);

		// Set or interpolate angles
		if (state->effects & EF_ROTATE)
		{
			AxisCopy(autoRotateAxis, ent.axis);

			// Make items bob up and down
			scale = 0.005f + cent->current.number * 0.00001f;
			ent.origin[2] += 4 + cos((cl.time + 1000) * scale) * 4;
		}
		else
		{
			LerpAngles(cent->prev.angles, cent->current.angles, cl.lerpFrac, angles);
			AnglesToAxis(angles, ent.axis);
		}

		// Set frame parameters for animation
		ent.frame = state->frame;

		ent.oldFrame = cent->prev.frame;
		ent.backLerp = 1.0f - cl.lerpFrac;

		// Add entity models for rendering
		CL_AddEntityModels(ent, cent, state);

        // Add automatic trails
        if (state->effects & ~EF_ROTATE)
			CL_AddEntityTrails(cent, &ent, state->effects);

		VectorCopy(ent.origin, cent->lerpOrigin);
	}
}

/*
 ==================
 CL_AddViewWeapon

 TODO: Shell effects do not seem to work
 BUG: Seems that if another player has a shell, you get it too
 ==================
*/
void CL_AddViewWeapon ()
{
    renderEntity_t	 ent;
	centity_t		 *cent;
	entity_state_t   *state;
	vec3_t			 angles;

	// Don't add if in wide angle view
	if (cl.playerState->fov > 90)
		return;

	// No gun model was found
    if (!cl.media.gameModels[cl.playerState->gunindex])
        return;

	state = &cl.entities[cl.playernum].current;

	cent = &cl.entities[cl.playernum];

	// Reset the entity
	memset(&ent, 0, sizeof(renderEntity_t));

	// Create a new entity
	ent.reType = RE_MODEL;
	ent.renderFX = RF_WEAPONMODEL;
	ent.depthHack = 0.3f;
	ent.model = cl.media.gameModels[cl.playerState->gunindex];
	ent.backLerp = 1.0f - cl.lerpFrac;
	ent.scale = 1;

	ent.materialParms[MATERIALPARM_RED] = 1.0f;
	ent.materialParms[MATERIALPARM_GREEN] = 1.0f;
	ent.materialParms[MATERIALPARM_BLUE] = 1.0f;
	ent.materialParms[MATERIALPARM_ALPHA] = 1.0f;
	ent.materialParms[MATERIALPARM_TIME_OFFSET] = cent->flashStartTime;
	ent.materialParms[MATERIALPARM_DIVERSITY] = cent->flashRotation;
	ent.materialParms[MATERIALPARM_GENERAL] = cl.playerState->stats[STAT_AMMO];
	ent.materialParms[MATERIALPARM_MODE] = 1.0f;

    // Set up gun position and angles
	VectorLerp(cl.oldPlayerState->gunoffset, cl.playerState->gunoffset, cl.lerpFrac, ent.origin);
	VectorAdd(ent.origin, cl.renderView.viewOrigin, ent.origin);

	LerpAngles(cl.oldPlayerState->gunangles, cl.playerState->gunangles, cl.lerpFrac, angles);
	VectorAdd(angles, cl.renderViewAngles, angles);
	AnglesToAxis(angles, ent.axis);

	// Set up gun frames
	ent.frame = cl.playerState->gunframe;
	ent.oldFrame = cl.oldPlayerState->gunframe;

	if (ent.frame == 0)
		ent.oldFrame = 0;   // Just changed weapons, don't lerp from old

	// Send it to the renderer
	R_AddEntityToScene(&ent);

	// Apply a shell effect if needed
	ent.renderFX |= state->renderfx;

	CL_AddShellEntity(&ent, cent, state->effects, false);
}